// 参数依赖查找（Argument-Dependent Lookup，ADL）详解
// 参数依赖查找（ADL），也被称为Koenig查找（以其发明者Andrew Koenig命名），是C++中一种特殊的名称查找机制，对于理解模板和泛型编程至关重要。

// 1.ADL的基本概念
// ADL是指：当调用一个不带命名空间限定符的函数时，编译器不仅在当前作用域中查找该函数，还会在函数参数的关联命名空间中查找。
// 简单来说，ADL让编译器能够"跟随"函数参数到它们的命名空间中寻找匹配的函数。

// 2.ADL的工作原理
// 2.1基本规则
// 当遇到形如f(x)的函数调用时，编译器会：
// 1)首先在常规作用域中查找函数f
// 2)然后在参数x的类型所在的命名空间中查找函数f
// 3)如果参数x是类类型，还会在其基类的命名空间中查找

// 示例
namespace NS
{
    struct MyType
    {
    };
    void func(MyType) { std::cout << "NS::func called\n"; }
}

void test()
{
    NS::MyType obj;
    func(obj); // 通过ADL找到NS::func
}
// 在这个例子中，尽管func没有使用命名空间限定符，编译器仍然能找到NS::func，因为参数obj的类型是NS::MyType。

// 3.ADL的关联命名空间
// 对于给定的类型T，其关联命名空间包括：
// 1)如果T是一个命名空间成员，则该命名空间
// 2)如果T是一个类成员，则该类的命名空间
// 3)如果T是一个类模板实例化，则模板参数的关联命名空间
// 4)如果T是一个类，则其基类的关联命名空间

// 复杂示例
namespace A
{
    struct X
    {
    };
}

namespace B
{
    struct Y : A::X
    {
    };
    void func(Y) { std::cout << "B::func(Y)\n"; }
}

namespace C
{
    void func(A::X) { std::cout << "C::func(X)\n"; }
}

void test()
{
    B::Y y;
    func(y); // 调用B::func(Y)，而不是C::func(X)
}
// 在这个例子中，尽管y是A::X的派生类，但ADL首先在B命名空间中找到了匹配的函数。

// 4.ADL与模板
// ADL在模板编程中尤为重要，它是C++标准库能够正常工作的关键机制之一。

// 4.1 标准库示例
std::vector<int> vec = {1, 2, 3};
std::sort(vec.begin(), vec.end());
// 这里，std::sort可以找到正确的operator<函数，即使它没有使用命名空间限定符，这是因为ADL会在迭代器和其值类型的命名空间中查找。

// 4.2 自定义类型示例
namespace MyLib
{
    class MyString
    {
    };
    std::ostream &operator<<(std::ostream &os, const MyString &str)
    {
        return os << "MyString";
    }
}

void print()
{
    MyLib::MyString str;
    std::cout << str; // 通过ADL找到MyLib::operator<<
}

// 4.3 ADL的特殊规则
// 4.3.1 仅适用于无限定函数名
// ADL只适用于无限定的函数名。如果使用了限定符，ADL不会发生：
namespace N
{
    struct S
    {
    };
    void f(S) {}
}

void g()
{
    N::S s;
    f(s);   // 通过ADL找到N::f
    ::f(s); // 错误：ADL不适用于限定名
}

// 4.3.2 友元函数声明
// 在类内部声明的友元函数会被注入到包含该类的命名空间中，这与ADL密切相关：
namespace N
{
    class C
    {
        friend void f(C) {} // 注入到命名空间N
    };
}

void test()
{
    N::C c;
    f(c); // 可以通过ADL找到
}

// 4.3.3 两阶段查找
// 对于模板中的函数调用，C++使用两阶段查找：
// 第一阶段：在模板定义时查找非依赖名称
// 第二阶段：在模板实例化时通过ADL查找依赖名称
template <typename T>
void process(T t)
{
    helper(t); // 依赖名称，通过ADL在实例化时查找
}

// 5.ADL的实际应用
// 4.1 操作符重载
// ADL使得操作符重载更加自然：
namespace Math
{
    struct Vector
    {
    };
    Vector operator+(const Vector &a, const Vector &b) { return Vector(); }
}

void calculate()
{
    Math::Vector a, b, c;
    c = a + b; // 通过ADL找到Math::operator+
}

// 4.2 自定义迭代器
// 实现与标准库兼容的自定义容器和迭代器：
namespace MyContainer
{
    template <typename T>
    class Iterator
    {
        // 迭代器实现
    };

    template <typename T>
    Iterator<T> begin(const Container<T> &c) { return Iterator<T>(c); }

    template <typename T>
    Iterator<T> end(const Container<T> &c) { return Iterator<T>(c, true); }
}

void process()
{
    MyContainer::Container<int> c;
    std::for_each(begin(c), end(c), [](int i) { /* ... */ });
    // 通过ADL找到MyContainer::begin和MyContainer::end
}

// 5.ADL的实际应用
// 5.1 操作符重载
namespace Math
{
    struct Vector
    {
    };
    Vector operator+(const Vector &a, const Vector &b) { return Vector(); }
}

void calculate()
{
    Math::Vector a, b, c;
    c = a + b; // 通过ADL找到Math::operator+
}

// 5.2 自定义迭代器
// 实现与标准库兼容的自定义容器和迭代器：
namespace MyContainer
{
    template <typename T>
    class Iterator
    {
        // 迭代器实现
    };

    template <typename T>
    Iterator<T> begin(const Container<T> &c) { return Iterator<T>(c); }

    template <typename T>
    Iterator<T> end(const Container<T> &c) { return Iterator<T>(c, true); }
}

void process()
{
    MyContainer::Container<int> c;
    std::for_each(begin(c), end(c), [](int i) { /* ... */ });
    // 通过ADL找到MyContainer::begin和MyContainer::end
}

// 5.3 SFINAE与ADL结合
// ADL可以与SFINAE（替换失败不是错误）结合使用，实现更高级的模板元编程技术：
namespace detail
{
    template <typename T>
    auto has_toString_impl(T &&t) -> decltype(toString(t), std::true_type{});

    std::false_type has_toString_impl(...);
}

template <typename T>
using has_toString = decltype(detail::has_toString_impl(std::declval<T>()));

// 6.ADL的注意事项
// 6.1 可能导致意外行为
// ADL有时会导致意外的函数调用：
namespace A
{
    struct X
    {
    };
    void func(X) { std::cout << "A::func\n"; }
}

void func(A::X) { std::cout << "global func\n"; }

void test()
{
    A::X x;
    func(x); // 调用A::func，而不是全局func
}

// 6.2 与using声明的交互
// ADL与using声明的交互可能导致复杂的重载解析：
namespace A
{
    struct X
    {
    };
    void f(X, int) {}
}

namespace B
{
    using A::f;
    void f(A::X, double) {}
}

void test()
{
    A::X x;
    B::f(x, 1); // 调用A::f(X, int)
}

// 7总结
// 参数依赖查找（ADL）是C++中一个强大而复杂的特性，它使得：
// 无需显式命名空间限定符就能调用与参数相关的函数
// 标准库容器和算法能够无缝协作
// 泛型编程更加自然和直观
// 操作符重载更加符合直觉
